package com.tender.utils;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author tender
 */
public class DataUtils {

    /**
     * 转换为指定类型列表
     *
     * @param src    源列表
     * @param mapper 转换方法
     * @param <S>    源类型
     * @param <D>    目标类型
     * @return 目标列表
     */
    public static <S, D> List<D> transform(List<S> src, Function<? super S, ? extends D> mapper) {
        if (CollectionUtils.isEmpty(src)) {
            return Collections.emptyList();
        }

        return src.stream().map(mapper).collect(Collectors.toList());
    }



    /**
     * 转换为指定类型列表(去重)
     *
     * @param src    源列表
     * @param mapper 转换方法
     * @param <S>    源类型
     * @param <D>    目标类型
     * @return 目标列表
     */
    public static <S, D> List<D> distinctTransform(List<S> src, Function<? super S, ? extends D> mapper) {
        if (CollectionUtils.isEmpty(src)) {
            return Collections.emptyList();
        }

        return src.stream().map(mapper).distinct().collect(Collectors.toList());
    }

    /**
     * 循环处理列表中的每个元素
     *
     * @param src    源列表
     * @param consumer 消费方法
     * @param <S>    源类型
     * @return 消费列表
     */
    public static <S> void consumerList(List<S> src, Consumer<S> consumer) {
        if (!CollectionUtils.isEmpty(src)) {
            src.forEach(consumer);
        }
    }

    /**
     * 按照指定方式进行分组
     *
     * @param src    源列表
     * @param mapper 转换方法
     * @param <S>    源类型
     * @param <D>    map 中key 的类型
     * @return 目标列表
     */

    public static <S, D> Map<D, List<S>> groupBy(List<S> src, Function<? super S, ? extends D> mapper) {
        if (CollectionUtils.isEmpty(src)) {
            return Collections.emptyMap();
        }

        return src.stream().collect(Collectors.groupingBy(mapper));
    }

    /**
     * 过滤符合要求的列表数据
     *
     * @param src       源列表
     * @param predicate 断言
     * @param <S>       源类型
     * @return 过滤后的列表
     */
    public static <S> List<S> filter(List<S> src, Predicate<S> predicate) {
        if (CollectionUtils.isEmpty(src)) {
            return Collections.emptyList();
        }

        return src.stream().filter(predicate).collect( Collectors.toList());
    }

    /**
     * 若值不为空字符串，则进行设置
     *
     * @param value
     * @param action
     */
    public static void setValueIfNotBlank(String value, Consumer<String> action) {
        setValue(value, StringUtils::isBlank, action);
    }

    /**
     * 若Integer 类型的值不为 null，则进行设置
     *
     * @param value
     * @param action
     */
    public static void setIntegerValueIfNotNull(Integer value, Consumer<Integer> action) {
        setValue(value, Objects::isNull, action);
    }

    /**
     * 若Integer 类型的值不为 null，则进行设置
     *
     * @param value
     * @param action
     */
    public static void setLongValueIfNotNull(Long value, Consumer<Long> action) {
        setValue(value, Objects::isNull, action);
    }

    /**
     * 若旧值为空字符串，则设置为新值
     *
     * @param oldValue 旧值
     * @param newValue 新值
     * @param action
     */
    public static void setValueIfBlank(String oldValue, String newValue, Consumer<String> action) {
        setValue(oldValue, newValue, StringUtils::isNotBlank, action);
    }

    /**
     * 将源信息设置到目标指定方法中
     *
     * @param src    源信息
     * @param ignore 忽略检查方法
     * @param action 目标设置方法
     * @param <T>
     */
    public static <T> void setValue(T src, Function<T, Boolean> ignore, Consumer<T> action) {
        if (ignore.apply(src)) {
            return;
        }
        action.accept(src);
    }

    /**
     * 将源信息设置到目标指定方法中
     *
     * @param oldValue 旧值
     * @param newValue 新值
     * @param ignore   忽略检查方法
     * @param action   目标设置方法
     * @param <T>      泛型
     */
    public static <T> void setValue(T oldValue, T newValue, Function<T, Boolean> ignore, Consumer<T> action) {
        if (ignore.apply(oldValue)) {
            return;
        }
        action.accept(newValue);
    }

    /**
     * 若值不为empty，则设置
     *
     * @param value
     * @param action
     */
    public static <T> void setListIfNotEmpty(List<T> value, Consumer<List<T>> action) {
        setValue(value, CollectionUtils::isEmpty, action);
    }

    public static String likeIn(String value) {
        return "%" + value + "%";
    }

    public static String likeAfter(String value) {
        return value + "%";
    }

    public static String likeBefore(String value) {
        return "%" + value;
    }

    /**
     * 对象属性拷贝
     *
     * @param source 源对象
     * @param clazz  目标对象类型
     * @param <T>    泛型，具体类型
     * @return
     */
    public static <T> T copyProperties(Object source, Class<T> clazz) {
        T target = null;
        try {
            target = clazz.newInstance();
        } catch (Exception e) {
            // do nothing;
        }

        if (null == source) {
            return target;
        }
        BeanUtils.copyProperties(source, target);

        return target;
    }

    @SuppressWarnings("unchecked")
    public static <T> T getValueWithReflex(String fieldName, Object object) throws Exception {
        PropertyDescriptor pd = new PropertyDescriptor(fieldName, object.getClass());
        Method readMethod = pd.getReadMethod();
        return (T) readMethod.invoke(object);
    }

    @SuppressWarnings("unchecked")
    public static void setValueWithReflex(String fieldName, Object value, Object object) throws Exception {
        PropertyDescriptor pd = new PropertyDescriptor(fieldName, object.getClass());
        Method writeMethod = pd.getWriteMethod();
        writeMethod.invoke(object, value);
    }

    /**
     * 求交集, 不改变原有集合中的元素
     *
     * @param source 数据源
     * @param target 目标数据
     * @return 交集
     */
    public static List<String> intersectionWithUnModify(List<String> source, List<String> target) {
        if (CollectionUtils.isEmpty(source) || CollectionUtils.isEmpty(target)) {
            return new ArrayList<>();
        }
        List<String> tmp = new ArrayList<>(source);
        tmp.retainAll(target);

        return tmp;
    }

    /**
     * 求交集, 改变原有集合中的元素
     *
     * @param source 数据源
     * @param target 目标数据
     * @return 交集
     */
    public static List<String> intersectionAndModify(List<String> source, List<String> target) {
        if (CollectionUtils.isEmpty(source) || CollectionUtils.isEmpty(target)) {
            return new ArrayList<>();
        }
        source.retainAll(target);

        return source;
    }

    /**
     * 通过反射获取所有的 field 集合
     *
     * @param clazz      目标对象的clazz
     * @param superClazz 递归跳出条件的clazz
     * @return
     */
    public static List<Field> getFieldsWithReflex(Class<?> clazz, Class<?> superClazz) {
        List<Field> result = new ArrayList<>();
        try {
            while (clazz != superClazz) {
                Field[] declaredFields = clazz.getDeclaredFields();
                result.addAll(Arrays.stream(declaredFields).collect(Collectors.toList()));
                clazz = clazz.getSuperclass();
            }
        } catch (Exception e) {
            // do nothing
        }

        return result;
    }

    /**
     * 通过反射获取所有的 field 集合
     *
     * @param clazz 目标对象的clazz
     */
    public static List<Field> getFieldsWithReflex(Class<?> clazz) {
        return getFieldsWithReflex(clazz, Object.class);
    }

    /**
     * @param object       要初始化的对象实例
     * @param clazz        要初始化字段的类型
     * @param defaultValue 对应类型的初始值
     * @throws Exception 需要由调用者自己处理
     */
    public static void initFieldsWithReflex(Object object, Class<?> clazz, Object defaultValue) throws Exception {
        List<Field> fields = getFieldsWithReflex(object.getClass());
        if (!CollectionUtils.isEmpty(fields)) {
            for (Field field : fields) {
                if (field.getType() == clazz) {
                    field.setAccessible(true);
                    field.set(object, defaultValue);
                }
            }
        }
    }

    /**
     * 用于 "单测" 使用
     * 时间字符串，需要自己重新覆盖
     * @param clazz
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static <T> T initInstanceAndSetDefaultValue(Class<T> clazz) throws Exception {
        T object = clazz.newInstance();
        List<Field> fields = getFieldsWithReflex(clazz);
        if (!CollectionUtils.isEmpty(fields)) {
            for (Field field : fields) {
                int modifiers = field.getModifiers();
                // public / private static final
                //    1   /    2      8     16
                if (modifiers == 25 || modifiers == 26 || modifiers == 17 || modifiers == 18 || modifiers == 24) {
                    continue;
                }
                field.setAccessible(true);
                Object value = getValueByType(field.getType());
                if (value == null) {
                    if (field.getType().isArray()) {
                        // 数组类型
                        Class<?> componentType = field.getType().getComponentType();
                        value = Array.newInstance(componentType, 3);
                        Array.set(value, 0, getValueByType(componentType));
                        Array.set(value, 1, getValueByType(componentType));
                        Array.set(value, 2, getValueByType(componentType));
                    } else if (List.class.isAssignableFrom(field.getType())) {
                        if (field.getGenericType() instanceof ParameterizedType) {
                            ParameterizedType type = (ParameterizedType) field.getGenericType();
                            Type[] actualTypeArguments = type.getActualTypeArguments();
                            Type actualTypeArgument = actualTypeArguments[0];
                            value = Lists.newArrayList(getValueByType((Class<?>) actualTypeArgument), getValueByType((Class<?>) actualTypeArgument));
                        } else {
                            value = Lists.newArrayList(new Object(), new Object());
                        }
                    } else if (Set.class.isAssignableFrom(field.getType())) {
                        if (field.getGenericType() instanceof ParameterizedType) {
                            ParameterizedType type = (ParameterizedType) field.getGenericType();
                            Type[] actualTypeArguments = type.getActualTypeArguments();
                            Type actualTypeArgument = actualTypeArguments[0];
                            value = Sets.newHashSet(getValueByType((Class<?>) actualTypeArgument), getValueByType((Class<?>) actualTypeArgument));
                        } else {
                            value = Sets.newHashSet(new Object(), new Object());
                        }
                    } else if (Map.class.isAssignableFrom(field.getType())) {
                        continue;
                    }
                }
                field.set(object, value);
            }
        }

        return object;
    }

    private static final Object getValueByType(Class<?> type) throws Exception {
        Object value = null;
        if (type == int.class || type == Integer.class) {
            value = 1;
        } else if (type == float.class || type == Float.class) {
            value = 1.0;
        } else if (type == double.class || type == Double.class) {
            value = 1.0;
        } else if (type == long.class || type == Long.class) {
            value = 1L;
        } else if (type == char.class || type == Character.class) {
            value = 97;
        } else if (type == byte.class || type == Byte.class) {
            value = 1;
        } else if (type == short.class || type == Short.class) {
            value = 1;
        } else if (type == boolean.class || type == Boolean.class) {
            value = true;
        } else if (type == BigDecimal.class) {
            value = new BigDecimal("1.11");
        } else if (type == BigInteger.class) {
            value = new BigInteger("10000");
        } else if (type == Date.class) {
            value = new Date();
        } else if (type == String.class) {
            value = "1000000";
        } else if (List.class.isAssignableFrom(type) || Set.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)) {
            return value;
        } else {
            // pojo 类型
            try {
                value = initInstanceAndSetDefaultValue(type);
            } catch (Exception e) {
                // do nothing
            }
        }

        return value;
    }

    /**
     * 根据正则表达式，多个匹配，但是一个一个的替换
     * （非全局替换）
     */
    public static String replaceOneByOneWithRegExp(String source, String regExp, Function<String, String> function) {
        Pattern p = Pattern.compile(regExp);
        Matcher m = p.matcher(source);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(sb, function.apply(m.group()));
        }
        m.appendTail(sb);

        return sb.toString();
    }

    /**
     * 获取泛型数组
     * @param ownClazz
     * @param targetClazz
     * @return
     */
    public static ResolvableType [] getGenericTypes (Class<?> ownClazz, Class<?> targetClazz) {
        Assert.notNull(ownClazz, "泛型承载clazz, 不能为空");
        Assert.notNull(targetClazz, "泛型目标clazz, 不能为空");
        return ResolvableType.forClass(ownClazz).as(targetClazz).getGenerics();
    }



//    public static void main(String[] args) {
//        String s = DataUtil.replaceOneByOneWithRegExp("one cat22 two cats in the cat11 yar", "cat\\d+", group -> {
//            return "dog" + new Random().nextInt(10);
//        });
//        System.out.println(s);
//    }

}
